#include "stdafx.h"

#include "json/jsoncpp/tool.hpp"
#include "json/jsoncpp/writer.hpp"
#include <iomanip>
#include <memory>
#include <sstream>
#include <utility>
#include <set>
#include <cassert>
#include <cstring>
#include <cstdio>

#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
#include <float.h>
#define isfinite _finite
#elif defined(__sun) && defined(__SVR4) //Solaris
#include <ieeefp.h>
#define isfinite finite
#else
#include <cmath>
#define isfinite std::isfinite
#endif

#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
#define snprintf _snprintf
#elif __cplusplus >= 201103L
#define snprintf std::snprintf
#endif

#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
// Disable warning about strdup being deprecated.
#pragma warning(disable : 4996)
#endif

namespace JHCPP
{
	namespace json
	{
		namespace jsoncpp
		{
		#if __cplusplus >= 201103L
			typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
		#else
			typedef std::auto_ptr<StreamWriter>   StreamWriterPtr;
		#endif

			static bool containsControlCharacter(const char* str)
			{
				while (*str) 
				{
					if (isControlCharacter(*(str++)))
						return true;
				}
				return false;
			}

			static bool containsControlCharacter0(const char* str, unsigned len) 
			{
				char const* end = str + len;
				while (end != str) 
				{
					if (isControlCharacter(*str) || 0==*str)
						return true;
					++str;
				}
				return false;
			}

			std::string valueToString(LargestInt value) 
			{
				UIntToStringBuffer buffer;
				char* current = buffer + sizeof(buffer);
				bool isNegative = value < 0;
				if (isNegative)
					value = -value;
				uintToString(LargestUInt(value), current);
				if (isNegative)
					*--current = '-';
				assert(current >= buffer);
				return current;
			}

			std::string valueToString(LargestUInt value)
			{
				UIntToStringBuffer buffer;
				char* current = buffer + sizeof(buffer);
				uintToString(value, current);
				assert(current >= buffer);
				return current;
			}

		#if defined(JSON_HAS_INT64)

			std::string valueToString(Int value) 
			{
				return valueToString(LargestInt(value));
			}

			std::string valueToString(UInt value) 
			{
				return valueToString(LargestUInt(value));
			}

		#endif // # if defined(JSON_HAS_INT64)

			std::string valueToString(double value) 
			{
				// Allocate a buffer that is more than large enough to store the 16 digits of
				// precision requested below.
				char buffer[32];
				int len = -1;

				// Print into the buffer. We need not request the alternative representation
				// that always has a decimal point because JSON doesn't distingish the
				// concepts of reals and integers.
			#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
				// visual studio 2005 to
				// avoid warning.
				len = sprintf_s(buffer, sizeof(buffer), "%.17g", value);
			#else
				if (isfinite(value)) 
				{
					len = snprintf(buffer, sizeof(buffer), "%.17g", value);
				} 
				else 
				{
					// IEEE standard states that NaN values will not compare to themselves
					if (value != value) {
						len = snprintf(buffer, sizeof(buffer), "null");
					} else if (value < 0) {
						len = snprintf(buffer, sizeof(buffer), "-1e+9999");
					} else {
						len = snprintf(buffer, sizeof(buffer), "1e+9999");
					}
					// For those, we do not need to call fixNumLoc, but it is fast.
				}
			#endif
				assert(len >= 0);
				fixNumericLocale(buffer, buffer + len);
				return buffer;
			}

			std::string valueToString(bool value) { return value ? "true" : "false"; }

			std::string valueToQuotedString(const char* value) 
			{
				if (value == NULL)
					return "";
				// Not sure how to handle unicode...
				if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
					!containsControlCharacter(value))
					return std::string("\"") + value + "\"";
				// We have to walk value and escape any special characters.
				// Appending to std::string is not efficient, but this should be rare.
				// (Note: forward slashes are *not* rare, but I am not escaping them.)
				std::string::size_type maxsize =
					strlen(value) * 2 + 3; // allescaped+quotes+NULL
				std::string result;
				result.reserve(maxsize); // to avoid lots of mallocs
				result += "\"";
				for (const char* c = value; *c != 0; ++c) {
					switch (*c) 
					{
					case '\"':
						result += "\\\"";
						break;
					case '\\':
						result += "\\\\";
						break;
					case '\b':
						result += "\\b";
						break;
					case '\f':
						result += "\\f";
						break;
					case '\n':
						result += "\\n";
						break;
					case '\r':
						result += "\\r";
						break;
					case '\t':
						result += "\\t";
						break;
						// case '/':
						// Even though \/ is considered a legal escape in JSON, a bare
						// slash is also legal, so I see no reason to escape it.
						// (I hope I am not misunderstanding something.
						// blep notes: actually escaping \/ may be useful in javascript to avoid </
						// sequence.
						// Should add a flag to allow this compatibility mode and prevent this
						// sequence from occurring.
					default:
						if (isControlCharacter(*c)) 
						{
							std::ostringstream oss;
							oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
								<< std::setw(4) << static_cast<int>(*c);
							result += oss.str();
						} 
						else 
						{
							result += *c;
						}
						break;
					}
				}
				result += "\"";
				return result;
			}

			// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
			static char const* strnpbrk(char const* s, char const* accept, size_t n) 
			{
				assert((s || !n) && accept);

				char const* const end = s + n;
				for (char const* cur = s; cur < end; ++cur)
				{
					int const c = *cur;
					for (char const* a = accept; *a; ++a) 
					{
						if (*a == c) 
						{
							return cur;
						}
					}
				}
				return NULL;
			}
			static std::string valueToQuotedStringN(const char* value, unsigned length) 
			{
				if (value == NULL)
					return "";
				// Not sure how to handle unicode...
				if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
					!containsControlCharacter0(value, length))
					return std::string("\"") + value + "\"";
				// We have to walk value and escape any special characters.
				// Appending to std::string is not efficient, but this should be rare.
				// (Note: forward slashes are *not* rare, but I am not escaping them.)
				std::string::size_type maxsize =
					length * 2 + 3; // allescaped+quotes+NULL
				std::string result;
				result.reserve(maxsize); // to avoid lots of mallocs
				result += "\"";
				char const* end = value + length;
				for (const char* c = value; c != end; ++c)
				{
					switch (*c) 
					{
					case '\"':
						result += "\\\"";
						break;
					case '\\':
						result += "\\\\";
						break;
					case '\b':
						result += "\\b";
						break;
					case '\f':
						result += "\\f";
						break;
					case '\n':
						result += "\\n";
						break;
					case '\r':
						result += "\\r";
						break;
					case '\t':
						result += "\\t";
						break;
						// case '/':
						// Even though \/ is considered a legal escape in JSON, a bare
						// slash is also legal, so I see no reason to escape it.
						// (I hope I am not misunderstanding something.)
						// blep notes: actually escaping \/ may be useful in javascript to avoid </
						// sequence.
						// Should add a flag to allow this compatibility mode and prevent this
						// sequence from occurring.
					default:
						if ((isControlCharacter(*c)) || (*c == 0)) {
							std::ostringstream oss;
							oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
								<< std::setw(4) << static_cast<int>(*c);
							result += oss.str();
						} else {
							result += *c;
						}
						break;
					}
				}
				result += "\"";
				return result;
			}

			// Class Writer
			// //////////////////////////////////////////////////////////////////
			Writer::~Writer() {}

			// Class FastWriter
			// //////////////////////////////////////////////////////////////////

			FastWriter::FastWriter()
				: m_yamlCompatiblityEnabled(false), m_dropNullPlaceholders(false),
				m_omitEndingLineFeed(false) {}

			void FastWriter::enableYAMLCompatibility() { m_yamlCompatiblityEnabled = true; }

			void FastWriter::dropNullPlaceholders() { m_dropNullPlaceholders = true; }

			void FastWriter::omitEndingLineFeed() { m_omitEndingLineFeed = true; }

			std::string FastWriter::write(const Value& root) 
			{
				m_document = "";
				writeValue(root);
				if (!m_omitEndingLineFeed)
					m_document += "\n";
				return m_document;
			}

			void FastWriter::writeValue(const Value& value) 
			{
				switch (value.type()) 
				{
				  case nullValue:
					  if (!m_dropNullPlaceholders)
						  m_document += "null";
					  break;
				  case intValue:
					  m_document += valueToString(value.asLargestInt());
					  break;
				  case uintValue:
					  m_document += valueToString(value.asLargestUInt());
					  break;
				  case realValue:
					  m_document += valueToString(value.asDouble());
					  break;
				  case stringValue:
					  {
						  // Is NULL possible for value.string_?
						  char const* str;
						  char const* end;
						  bool ok = value.getString(&str, &end);
						  if (ok) 
							  m_document += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
						  break;
					  }
				  case booleanValue:
					  m_document += valueToString(value.asBool());
					  break;
				  case arrayValue: 
					  {
						  m_document += '[';
						  int size = value.size();
						  for (int index = 0; index < size; ++index) 
						  {
							  if (index > 0)
								  m_document += ',';
							  writeValue(value[index]);
						  }
						  m_document += ']';		  
					  } 
					  break;
				  case objectValue:
					  {
						  Value::Members members(value.getMemberNames());
						  m_document += '{';
						  for (Value::Members::iterator it = members.begin(); it != members.end(); ++it) 
						  {
							  const std::string& name = *it;
							  if (it != members.begin())
								  m_document += ',';
							  m_document += valueToQuotedStringN(name.data(), name.length());
							  m_document += m_yamlCompatiblityEnabled ? ": " : ":";
							  writeValue(value[name]);
						  }
						  m_document += '}';
					 } 
					 break;
				}
			}

			// Class StyledWriter
			// //////////////////////////////////////////////////////////////////

			StyledWriter::StyledWriter()
				: m_rightMargin(74), m_indentSize(3), m_addChildValues() {}

			std::string StyledWriter::write(const Value& root)
			{
				m_document = "";
				m_addChildValues = false;
				m_indentString = "";
				writeCommentBeforeValue(root);
				writeValue(root);
				writeCommentAfterValueOnSameLine(root);
				m_document += "\n";
				return m_document;
			}

			void StyledWriter::writeValue(const Value& value) 
			{
				switch (value.type()) 
				{
				  case nullValue:
					  pushValue("null");
					  break;
				  case intValue:
					  pushValue(valueToString(value.asLargestInt()));
					  break;
				  case uintValue:
					  pushValue(valueToString(value.asLargestUInt()));
					  break;
				  case realValue:
					  pushValue(valueToString(value.asDouble()));
					  break;
				  case stringValue:
					  {
						  // Is NULL possible for value.string_?
						  char const* str;
						  char const* end;
						  bool ok = value.getString(&str, &end);
						  if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
						  else pushValue("");
						  break;
					  }
				  case booleanValue:
					  pushValue(valueToString(value.asBool()));
					  break;
				  case arrayValue:
					  writeArrayValue(value);
					  break;
				  case objectValue:
					  {
						 Value::Members members(value.getMemberNames());
						if (members.empty())
							pushValue("{}");
						else 
						{
							writeWithIndent("{");
							indent();
							Value::Members::iterator it = members.begin();
							for (;;) 
							{
								const std::string& name = *it;
								const Value& childValue = value[name];
								writeCommentBeforeValue(childValue);
								writeWithIndent(valueToQuotedString(name.c_str()));
								m_document += " : ";
								writeValue(childValue);
								if (++it == members.end())
								{
									writeCommentAfterValueOnSameLine(childValue);
									break;
								}
								m_document += ',';
								writeCommentAfterValueOnSameLine(childValue);
							}
							unindent();
							writeWithIndent("}");
						}		
					 } 
					 break;
				}
			}

			void StyledWriter::writeArrayValue(const Value& value)
			{
				unsigned size = value.size();
				if (size == 0)
					pushValue("[]");
				else {
					bool isArrayMultiLine = isMultineArray(value);
					if (isArrayMultiLine) {
						writeWithIndent("[");
						indent();
						bool hasChildValue = !m_childValues.empty();
						unsigned index = 0;
						for (;;) {
							const Value& childValue = value[index];
							writeCommentBeforeValue(childValue);
							if (hasChildValue)
								writeWithIndent(m_childValues[index]);
							else {
								writeIndent();
								writeValue(childValue);
							}
							if (++index == size) {
								writeCommentAfterValueOnSameLine(childValue);
								break;
							}
							m_document += ',';
							writeCommentAfterValueOnSameLine(childValue);
						}
						unindent();
						writeWithIndent("]");
					} else // output on a single line
					{
						assert(m_childValues.size() == size);
						m_document += "[ ";
						for (unsigned index = 0; index < size; ++index) {
							if (index > 0)
								m_document += ", ";
							m_document += m_childValues[index];
						}
						m_document += " ]";
					}
				}
			}

			bool StyledWriter::isMultineArray(const Value& value) 
			{
				int size = value.size();
				bool isMultiLine = size * 3 >= m_rightMargin;
				m_childValues.clear();
				for (int index = 0; index < size && !isMultiLine; ++index) {
					const Value& childValue = value[index];
					isMultiLine =
						isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
						childValue.size() > 0);
				}
				if (!isMultiLine) // check if line length > max line length
				{
					m_childValues.reserve(size);
					m_addChildValues = true;
					int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
					for (int index = 0; index < size; ++index) {
						if (hasCommentForValue(value[index])) {
							isMultiLine = true;
						}
						writeValue(value[index]);
						lineLength += int(m_childValues[index].length());
					}
					m_addChildValues = false;
					isMultiLine = isMultiLine || lineLength >= m_rightMargin;
				}
				return isMultiLine;
			}

			void StyledWriter::pushValue(const std::string& value) 
			{
				if (m_addChildValues)
					m_childValues.push_back(value);
				else
					m_document += value;
			}

			void StyledWriter::writeIndent() 
			{
				if (!m_document.empty()) {
					char last = m_document[m_document.length() - 1];
					if (last == ' ') // already indented
						return;
					if (last != '\n') // Comments may add new-line
						m_document += '\n';
				}
				m_document += m_indentString;
			}

			void StyledWriter::writeWithIndent(const std::string& value)
			{
				writeIndent();
				m_document += value;
			}

			void StyledWriter::indent() { m_indentString += std::string(m_indentSize, ' '); }

			void StyledWriter::unindent() 
			{
				assert(int(m_indentString.size()) >= m_indentSize);
				m_indentString.resize(m_indentString.size() - m_indentSize);
			}

			void StyledWriter::writeCommentBeforeValue(const Value& root) 
			{
				if (!root.hasComment(commentBefore))
					return;

				m_document += "\n";
				writeIndent();
				const std::string& comment = root.getComment(commentBefore);
				std::string::const_iterator iter = comment.begin();
				while (iter != comment.end()) 
				{
					m_document += *iter;
					if (*iter == '\n' &&
						(iter != comment.end() && *(iter + 1) == '/'))
						writeIndent();
					++iter;
				}

				// Comments are stripped of trailing newlines, so add one here
				m_document += "\n";
			}

			void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) 
			{
				if (root.hasComment(commentAfterOnSameLine))
					m_document += " " + root.getComment(commentAfterOnSameLine);

				if (root.hasComment(commentAfter)) {
					m_document += "\n";
					m_document += root.getComment(commentAfter);
					m_document += "\n";
				}
			}

			bool StyledWriter::hasCommentForValue(const Value& value) {
				return value.hasComment(commentBefore) ||
					value.hasComment(commentAfterOnSameLine) ||
					value.hasComment(commentAfter);
			}

			// Class StyledStreamWriter
			// //////////////////////////////////////////////////////////////////

			StyledStreamWriter::StyledStreamWriter(std::string indentation)
				: m_document(NULL), m_rightMargin(74), m_indentation(indentation),
				m_addChildValues() {}

			void StyledStreamWriter::write(std::ostream& out, const Value& root) 
			{
				m_document = &out;
				m_addChildValues = false;
				m_indentString = "";
				m_indented = true;
				writeCommentBeforeValue(root);
				if (!m_indented) writeIndent();
				m_indented = true;
				writeValue(root);
				writeCommentAfterValueOnSameLine(root);
				*m_document << "\n";
				m_document = NULL; // Forget the stream, for safety.
			}

			void StyledStreamWriter::writeValue(const Value& value) 
			{
				switch (value.type()) 
				{
				  case nullValue:
					  pushValue("null");
					  break;
				  case intValue:
					  pushValue(valueToString(value.asLargestInt()));
					  break;
				  case uintValue:
					  pushValue(valueToString(value.asLargestUInt()));
					  break;
				  case realValue:
					  pushValue(valueToString(value.asDouble()));
					  break;
				  case stringValue:
					  {
						  // Is NULL possible for value.string_?
						  char const* str;
						  char const* end;
						  bool ok = value.getString(&str, &end);
						  if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
						  else pushValue("");
						  break;
					  }
				  case booleanValue:
					  pushValue(valueToString(value.asBool()));
					  break;
				  case arrayValue:
					  writeArrayValue(value);
					  break;
				  case objectValue: 
					{
						  Value::Members members(value.getMemberNames());
						  if (members.empty())
							  pushValue("{}");
						  else {
							  writeWithIndent("{");
							  indent();
							  Value::Members::iterator it = members.begin();
							  for (;;) {
								  const std::string& name = *it;
								  const Value& childValue = value[name];
								  writeCommentBeforeValue(childValue);
								  writeWithIndent(valueToQuotedString(name.c_str()));
								  *m_document << " : ";
								  writeValue(childValue);
								  if (++it == members.end()) {
									  writeCommentAfterValueOnSameLine(childValue);
									  break;
								  }
								  *m_document << ",";
								  writeCommentAfterValueOnSameLine(childValue);
							  }
							  unindent();
							  writeWithIndent("}");
						  }				
					} break;
				}
			}

			void StyledStreamWriter::writeArrayValue(const Value& value) 
			{
				unsigned size = value.size();
				if (size == 0)
					pushValue("[]");
				else {
					bool isArrayMultiLine = isMultineArray(value);
					if (isArrayMultiLine)
					{
						writeWithIndent("[");
						indent();
						bool hasChildValue = !m_childValues.empty();
						unsigned index = 0;
						for (;;) {
							const Value& childValue = value[index];
							writeCommentBeforeValue(childValue);
							if (hasChildValue)
								writeWithIndent(m_childValues[index]);
							else {
								if (!m_indented) writeIndent();
								m_indented = true;
								writeValue(childValue);
								m_indented = false;
							}
							if (++index == size) {
								writeCommentAfterValueOnSameLine(childValue);
								break;
							}
							*m_document << ",";
							writeCommentAfterValueOnSameLine(childValue);
						}
						unindent();
						writeWithIndent("]");
					} else // output on a single line
					{
						assert(m_childValues.size() == size);
						*m_document << "[ ";
						for (unsigned index = 0; index < size; ++index) {
							if (index > 0)
								*m_document << ", ";
							*m_document << m_childValues[index];
						}
						*m_document << " ]";
					}
				}
			}

			bool StyledStreamWriter::isMultineArray(const Value& value) 
			{
				int size = value.size();
				bool isMultiLine = size * 3 >= m_rightMargin;
				m_childValues.clear();
				for (int index = 0; index < size && !isMultiLine; ++index) 
				{
					const Value& childValue = value[index];
					isMultiLine =
						isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
						childValue.size() > 0);
				}
				if (!isMultiLine) // check if line length > max line length
				{
					m_childValues.reserve(size);
					m_addChildValues = true;
					int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
					for (int index = 0; index < size; ++index) {
						if (hasCommentForValue(value[index])) {
							isMultiLine = true;
						}
						writeValue(value[index]);
						lineLength += int(m_childValues[index].length());
					}
					m_addChildValues = false;
					isMultiLine = isMultiLine || lineLength >= m_rightMargin;
				}
				return isMultiLine;
			}

			void StyledStreamWriter::pushValue(const std::string& value) 
			{
				if (m_addChildValues)
					m_childValues.push_back(value);
				else
					*m_document << value;
			}

			void StyledStreamWriter::writeIndent() 
			{
				// blep intended this to look at the so-far-written string
				// to determine whether we are already indented, but
				// with a stream we cannot do that. So we rely on some saved state.
				// The caller checks indented_.
				*m_document << '\n' << m_indentString;
			}

			void StyledStreamWriter::writeWithIndent(const std::string& value) 
			{
				if (!m_indented) writeIndent();
				*m_document << value;
				m_indented = false;
			}

			void StyledStreamWriter::indent() { m_indentString += m_indentation; }

			void StyledStreamWriter::unindent() {
				assert(m_indentString.size() >= m_indentation.size());
				m_indentString.resize(m_indentString.size() - m_indentation.size());
			}

			void StyledStreamWriter::writeCommentBeforeValue(const Value& root) 
			{
				if (!root.hasComment(commentBefore))
					return;

				if (!m_indented) writeIndent();
				const std::string& comment = root.getComment(commentBefore);
				std::string::const_iterator iter = comment.begin();
				while (iter != comment.end()) {
					*m_document << *iter;
					if (*iter == '\n' &&
						(iter != comment.end() && *(iter + 1) == '/'))
						// writeIndent();  // would include newline
						*m_document << m_indentString;
					++iter;
				}
				m_indented = false;
			}

			void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) 
			{
				if (root.hasComment(commentAfterOnSameLine))
					*m_document << ' ' << root.getComment(commentAfterOnSameLine);

				if (root.hasComment(commentAfter)) {
					writeIndent();
					*m_document << root.getComment(commentAfter);
				}
				m_indented = false;
			}

			bool StyledStreamWriter::hasCommentForValue(const Value& value) {
				return value.hasComment(commentBefore) ||
					value.hasComment(commentAfterOnSameLine) ||
					value.hasComment(commentAfter);
			}

			//////////////////////////
			// BuiltStyledStreamWriter

			/// Scoped enums are not available until C++11.
			struct CommentStyle {
				/// Decide whether to write comments.
				enum Enum {
					None,  ///< Drop all comments.
					Most,  ///< Recover odd behavior of previous versions (not implemented yet).
					All  ///< Keep all comments.
				};
			};

			struct BuiltStyledStreamWriter : public StreamWriter
			{
				BuiltStyledStreamWriter(
					std::string const& indentation,
					CommentStyle::Enum cs,
					std::string const& colonSymbol,
					std::string const& nullSymbol,
					std::string const& endingLineFeedSymbol);
				virtual int write(Value const& root, std::ostream* sout);
			private:
				void writeValue(Value const& value);
				void writeArrayValue(Value const& value);
				bool isMultineArray(Value const& value);
				void pushValue(std::string const& value);
				void writeIndent();
				void writeWithIndent(std::string const& value);
				void indent();
				void unindent();
				void writeCommentBeforeValue(Value const& root);
				void writeCommentAfterValueOnSameLine(Value const& root);
				static bool hasCommentForValue(const Value& value);

				typedef std::vector<std::string> ChildValues;

				ChildValues childValues_;
				std::string indentString_;
				int rightMargin_;
				std::string indentation_;
				CommentStyle::Enum cs_;
				std::string colonSymbol_;
				std::string nullSymbol_;
				std::string endingLineFeedSymbol_;
				bool addChildValues_ : 1;
				bool indented_ : 1;
			};
			BuiltStyledStreamWriter::BuiltStyledStreamWriter(
				std::string const& indentation,
				CommentStyle::Enum cs,
				std::string const& colonSymbol,
				std::string const& nullSymbol,
				std::string const& endingLineFeedSymbol)
				: rightMargin_(74)
				, indentation_(indentation)
				, cs_(cs)
				, colonSymbol_(colonSymbol)
				, nullSymbol_(nullSymbol)
				, endingLineFeedSymbol_(endingLineFeedSymbol)
				, addChildValues_(false)
				, indented_(false)
			{
			}
			int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)
			{
				m_sout = sout;
				addChildValues_ = false;
				indented_ = true;
				indentString_ = "";
				writeCommentBeforeValue(root);
				if (!indented_) writeIndent();
				indented_ = true;
				writeValue(root);
				writeCommentAfterValueOnSameLine(root);
				*m_sout << endingLineFeedSymbol_;
				m_sout = NULL;
				return 0;
			}

			void BuiltStyledStreamWriter::writeValue(Value const& value) 
			{
				switch (value.type()) 
				{
			  case nullValue:
				  pushValue(nullSymbol_);
				  break;
			  case intValue:
				  pushValue(valueToString(value.asLargestInt()));
				  break;
			  case uintValue:
				  pushValue(valueToString(value.asLargestUInt()));
				  break;
			  case realValue:
				  pushValue(valueToString(value.asDouble()));
				  break;
			  case stringValue:
				  {
					  // Is NULL is possible for value.string_?
					  char const* str;
					  char const* end;
					  bool ok = value.getString(&str, &end);
					  if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
					  else pushValue("");
					  break;
				  }
			  case booleanValue:
				  pushValue(valueToString(value.asBool()));
				  break;
			  case arrayValue:
				  writeArrayValue(value);
				  break;
			  case objectValue: 
				  {
						Value::Members members(value.getMemberNames());
						if (members.empty())
							pushValue("{}");
						else
						{
							writeWithIndent("{");
							indent();
							Value::Members::iterator it = members.begin();
							for (;;) 
							{
								std::string const& name = *it;
								Value const& childValue = value[name];
								writeCommentBeforeValue(childValue);
								writeWithIndent(valueToQuotedStringN(name.data(), name.length()));
								*m_sout << colonSymbol_;
								 writeValue(childValue);
								if (++it == members.end()) 
								{
									writeCommentAfterValueOnSameLine(childValue);
									break;
								}
								*m_sout << ",";
								writeCommentAfterValueOnSameLine(childValue);
							}
							unindent();
							writeWithIndent("}");
						 }
					}
					break;
				}
			}

			void BuiltStyledStreamWriter::writeArrayValue(Value const& value) 
			{
				unsigned size = value.size();
				if (size == 0)
					pushValue("[]");
				else {
					bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
					if (isMultiLine) {
						writeWithIndent("[");
						indent();
						bool hasChildValue = !childValues_.empty();
						unsigned index = 0;
						for (;;) {
							Value const& childValue = value[index];
							writeCommentBeforeValue(childValue);
							if (hasChildValue)
								writeWithIndent(childValues_[index]);
							else {
								if (!indented_) writeIndent();
								indented_ = true;
								writeValue(childValue);
								indented_ = false;
							}
							if (++index == size) {
								writeCommentAfterValueOnSameLine(childValue);
								break;
							}
							*m_sout << ",";
							writeCommentAfterValueOnSameLine(childValue);
						}
						unindent();
						writeWithIndent("]");
					} else // output on a single line
					{
						assert(childValues_.size() == size);
						*m_sout << "[";
						if (!indentation_.empty()) *m_sout << " ";
						for (unsigned index = 0; index < size; ++index) {
							if (index > 0)
								*m_sout << ", ";
							*m_sout << childValues_[index];
						}
						if (!indentation_.empty()) *m_sout << " ";
						*m_sout << "]";
					}
				}
			}

			bool BuiltStyledStreamWriter::isMultineArray(Value const& value) 
			{
				int size = value.size();
				bool isMultiLine = size * 3 >= rightMargin_;
				childValues_.clear();
				for (int index = 0; index < size && !isMultiLine; ++index) {
					Value const& childValue = value[index];
					isMultiLine =
						isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
						childValue.size() > 0);
				}
				if (!isMultiLine) // check if line length > max line length
				{
					childValues_.reserve(size);
					addChildValues_ = true;
					int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
					for (int index = 0; index < size; ++index) {
						if (hasCommentForValue(value[index])) {
							isMultiLine = true;
						}
						writeValue(value[index]);
						lineLength += int(childValues_[index].length());
					}
					addChildValues_ = false;
					isMultiLine = isMultiLine || lineLength >= rightMargin_;
				}
				return isMultiLine;
			}

			void BuiltStyledStreamWriter::pushValue(std::string const& value) 
			{
				if (addChildValues_)
					childValues_.push_back(value);
				else
					*m_sout << value;
			}

			void BuiltStyledStreamWriter::writeIndent() 
			{
				// blep intended this to look at the so-far-written string
				// to determine whether we are already indented, but
				// with a stream we cannot do that. So we rely on some saved state.
				// The caller checks indented_.

				if (!indentation_.empty()) {
					// In this case, drop newlines too.
					*m_sout << '\n' << indentString_;
				}
			}

			void BuiltStyledStreamWriter::writeWithIndent(std::string const& value)
			{
				if (!indented_) writeIndent();
				*m_sout << value;
				indented_ = false;
			}

			void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }

			void BuiltStyledStreamWriter::unindent() {
				assert(indentString_.size() >= indentation_.size());
				indentString_.resize(indentString_.size() - indentation_.size());
			}

			void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root)
			{
				if (cs_ == CommentStyle::None) return;
				if (!root.hasComment(commentBefore))
					return;

				if (!indented_) writeIndent();
				const std::string& comment = root.getComment(commentBefore);
				std::string::const_iterator iter = comment.begin();
				while (iter != comment.end()) {
					*m_sout << *iter;
					if (*iter == '\n' &&
						(iter != comment.end() && *(iter + 1) == '/'))
						// writeIndent();  // would write extra newline
						*m_sout << indentString_;
					++iter;
				}
				indented_ = false;
			}

			void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) 
			{
				if (cs_ == CommentStyle::None) return;
				if (root.hasComment(commentAfterOnSameLine))
					*m_sout << " " + root.getComment(commentAfterOnSameLine);

				if (root.hasComment(commentAfter)) {
					writeIndent();
					*m_sout << root.getComment(commentAfter);
				}
			}

			// static
			bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
				return value.hasComment(commentBefore) ||
					value.hasComment(commentAfterOnSameLine) ||
					value.hasComment(commentAfter);
			}

			///////////////
			// StreamWriter

			StreamWriter::StreamWriter()
				: m_sout(NULL)
			{
			}
			StreamWriter::~StreamWriter()
			{
			}
			StreamWriter::Factory::~Factory()
			{}
			StreamWriterBuilder::StreamWriterBuilder()
			{
				setDefaults(&m_settings);
			}
			StreamWriterBuilder::~StreamWriterBuilder()
			{}
			StreamWriter* StreamWriterBuilder::newStreamWriter() const
			{
				std::string indentation = m_settings["indentation"].asString();
				std::string cs_str = m_settings["commentStyle"].asString();
				bool eyc = m_settings["enableYAMLCompatibility"].asBool();
				bool dnp = m_settings["dropNullPlaceholders"].asBool();
				CommentStyle::Enum cs = CommentStyle::All;
				if (cs_str == "All") {
					cs = CommentStyle::All;
				} else if (cs_str == "None") {
					cs = CommentStyle::None;
				} else {
					throwRuntimeError("commentStyle must be 'All' or 'None'");
				}
				std::string colonSymbol = " : ";
				if (eyc) {
					colonSymbol = ": ";
				} else if (indentation.empty()) {
					colonSymbol = ":";
				}
				std::string nullSymbol = "null";
				if (dnp) {
					nullSymbol = "";
				}
				std::string endingLineFeedSymbol = "";
				return new BuiltStyledStreamWriter(
					indentation, cs,
					colonSymbol, nullSymbol, endingLineFeedSymbol);
			}

			static void getValidWriterKeys(std::set<std::string>* valid_keys)
			{
				valid_keys->clear();
				valid_keys->insert("indentation");
				valid_keys->insert("commentStyle");
				valid_keys->insert("enableYAMLCompatibility");
				valid_keys->insert("dropNullPlaceholders");
			}

			bool StreamWriterBuilder::validate(Value* invalid) const
			{
				Value my_invalid;
				if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL
				Value& inv = *invalid;
				std::set<std::string> valid_keys;
				getValidWriterKeys(&valid_keys);
				Value::Members keys = m_settings.getMemberNames();
				size_t n = keys.size();
				for (size_t i = 0; i < n; ++i) 
				{
					std::string const& key = keys[i];
					if (valid_keys.find(key) == valid_keys.end()) 
					{
						inv[key] = m_settings[key];
					}
				}
				return 0u == inv.size();
			}

			Value& StreamWriterBuilder::operator[](std::string key)
			{
				return m_settings[key];
			}
			// static
			void StreamWriterBuilder::setDefaults(Value* settings)
			{
				//! [StreamWriterBuilderDefaults]
				(*settings)["commentStyle"] = "All";
				(*settings)["indentation"] = "\t";
				(*settings)["enableYAMLCompatibility"] = false;
				(*settings)["dropNullPlaceholders"] = false;
				//! [StreamWriterBuilderDefaults]
			}

			std::string writeString(StreamWriter::Factory const& builder, Value const& root) 
			{
				std::ostringstream sout;
				StreamWriterPtr const writer(builder.newStreamWriter());
				writer->write(root, &sout);
				return sout.str();
			}

			std::ostream& operator<<(std::ostream& sout, Value const& root) 
			{
				StreamWriterBuilder builder;
				StreamWriterPtr const writer(builder.newStreamWriter());
				writer->write(root, &sout);
				return sout;
			}
		}//end namespace jsoncpp
	}//end namespace json
}//end namespace JHCPP